﻿using AutoMapper;
using IdentityModel.Client;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Threading.Tasks;
using Wuther.Api.ActionConstraints;
using Wuther.Bussiness.Interface;
using Wuther.Entities.Models;
using Wuther.Util.DtoParameters;
using Wuther.Util.Enums;
using Wuther.Util.Helper;
using Wuther.Util.Models;
using Wuther.Util.PropertyMapping;

namespace Wuther.Api.Controllers
{
    [Route("api/[controller]"), ApiExplorerSettings(GroupName = "Calligraphy")]
    [ApiController]
    public class CalligraphyController : BaseController
    {
        private readonly IMapper _mapper;
        private readonly ICalligraphyRepository _calligraphyRepository;
        private readonly IConfiguration _configuration;
        private readonly IPropertyMappingService _propertyMappingService;
        private readonly IPropertyCheckerService _propertyCheckerService;
        private readonly ILogger<CalligraphyController> _log;

        public CalligraphyController(IMapper mapper,
            ICalligraphyRepository calligraphyRepository,
            IPropertyMappingService propertyMappingService,
            IPropertyCheckerService propertyCheckerService,
            ILogger<CalligraphyController> log,
            IConfiguration configuration)
        {
            _mapper = mapper;
            _calligraphyRepository = calligraphyRepository;
            _propertyMappingService = propertyMappingService;
            _propertyCheckerService = propertyCheckerService;
            _log = log;
            _configuration = configuration;
        }

        [Produces("application/json",
                    "application/vnd.wuther.hateoas+json",
                    "application/vnd.wuther.calligraphy.friendly+json",
                    "application/vnd.wuther.calligraphy.friendly.hateoas+json",
                    "application/vnd.wuther.calligraphy.full+json",
                    "application/vnd.wuther.calligraphy.full.hateoas+json")]
        [HttpGet("{CalligraphyId}", Name = nameof(GetCalligraphy))]
        public async Task<IActionResult> GetCalligraphy(int CalligraphyId, string fields, [FromHeader(Name = "Accept")] string mediaType)
        {
            if (!MediaTypeHeaderValue.TryParse(mediaType, out MediaTypeHeaderValue parsedMediaType))
            {
                return BadRequest();
            }
            if (!_propertyCheckerService.TypeHasProperties<CalligraphyDto>(fields))
            {
                return BadRequest();
            }
            var includeLinks = parsedMediaType.SubTypeWithoutSuffix.
                EndsWith("hateoas", System.StringComparison.InvariantCultureIgnoreCase);
            IEnumerable<LinkDto> myLinks = new List<LinkDto>();
            if (includeLinks)
            {
                myLinks = CreateLinksForCalligraphys(CalligraphyId, fields);
            }

            var primaryMediaType = includeLinks
                ? parsedMediaType.SubTypeWithoutSuffix.Substring(0, parsedMediaType.SubTypeWithoutSuffix.Length - 8)
                : parsedMediaType.SubTypeWithoutSuffix;
            if (primaryMediaType == "vnd.wuther.calligraphy.full")
            {
                var calligraphy = await _calligraphyRepository.GetCalligraphyAsync(CalligraphyId);
                if (calligraphy == null)
                {
                    return NotFound();
                }
                var full = _mapper.Map<Calligraphy, CalligraphyFullDto>(calligraphy);
                var fullDic = full.ShapeData(fields) as IDictionary<string, object>;
                if (includeLinks)
                {
                    fullDic.Add("links", myLinks);
                }
                return Ok(full);
            }
            else
            {
                var Calligraphy = await _calligraphyRepository.GetCalligraphyAsync(CalligraphyId);
                if (Calligraphy == null)
                {
                    return NotFound();
                }
                var friendly = _mapper.Map<CalligraphyDto>(Calligraphy).ShapeData(fields) as IDictionary<string, object>;

                if (includeLinks)
                {
                    friendly.Add("links", myLinks);
                }

                return Ok(friendly);
            }
        }


        [Produces("application/json",
            "application/vnd.wuther.hateoas+json",
            "application/vnd.wuther.calligraphy.friendly+json",
            "application/vnd.wuther.calligraphy.friendly.hateoas+json",
            "application/vnd.wuther.calligraphy.full+json",
            "application/vnd.wuther.calligraphy.full.hateoas+json")]
        [HttpGet(Name = nameof(GetCalligraphys))]
        public async Task<IActionResult> GetCalligraphys([FromQuery] DtoCalligraphyParameter parameter, [FromHeader(Name = "Accept")] string mediaType)
        {
            if (!MediaTypeHeaderValue.TryParse(mediaType, out MediaTypeHeaderValue parsedMediaType))
            {
                return BadRequest();
            }
            if (!_propertyCheckerService.TypeHasProperties<CalligraphyDto>(parameter.Fields))
            {
                return BadRequest();
            }
            if (!_propertyMappingService.ValidMappingExistsFor<CalligraphyDto, Calligraphy>(parameter.OrderBy))
            {
                return BadRequest();
            }
            var includeLinks = parsedMediaType.SubTypeWithoutSuffix.
                EndsWith("hateoas", System.StringComparison.InvariantCultureIgnoreCase);
            IEnumerable<LinkDto> myLinks = new List<LinkDto>();
            var primaryMediaType = includeLinks
                ? parsedMediaType.SubTypeWithoutSuffix.Substring(0, parsedMediaType.SubTypeWithoutSuffix.Length - 8)
                : parsedMediaType.SubTypeWithoutSuffix;


            if (primaryMediaType == "vnd.wuther.calligraphy.full")
            {
                var fullCalligraphys = await _calligraphyRepository.GetCalligraphysAsync(parameter);
                var fullPaginationMetadata = new
                {
                    totalCount = fullCalligraphys.TotalCount,
                    pageSize = fullCalligraphys.PageSize,
                    currentPage = fullCalligraphys.CurrentPage,
                    totalPages = fullCalligraphys.TotalPages,
                };
                Response.Headers.Add("X-Pagination", JsonSerializer.Serialize(fullPaginationMetadata, new JsonSerializerOptions
                {
                    Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
                }));
                var blogFullDtos = _mapper.Map<IList<CalligraphyFullDto>>(fullCalligraphys);
                var shapedFullData = blogFullDtos.shapeData(parameter.Fields);
                var fullLinks = CreateLinksForMenus(parameter, fullCalligraphys.HasPrevious, fullCalligraphys.HasNext);
                var fullLinkedCollectionResource = new
                {
                    value = shapedFullData,
                    fullLinks
                };

                return Ok(fullLinkedCollectionResource);

            }
            var Calligraphys = await _calligraphyRepository.GetCalligraphysAsync(parameter);
            var paginationMetadata = new
            {
                totalCount = Calligraphys.TotalCount,
                pageSize = Calligraphys.PageSize,
                currentPage = Calligraphys.CurrentPage,
                totalPages = Calligraphys.TotalPages,
            };
            Response.Headers.Add("X-Pagination", JsonSerializer.Serialize(paginationMetadata, new JsonSerializerOptions
            {
                Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
            }));

            var CalligraphyDtos = _mapper.Map<IList<CalligraphyDto>>(Calligraphys);
            var shapedData = CalligraphyDtos.shapeData(parameter.Fields);
            var links = CreateLinksForMenus(parameter, Calligraphys.HasPrevious, Calligraphys.HasNext);

            //var shapedMenusWithLinks = shapedData.Select(c =>
            //{
            //    var menuDict = c as IDictionary<string, object>;
            //    var menusLinks = CreateLinksForMenus((int)menuDict["Id"], null);
            //    menuDict.Add("links", menusLinks);
            //    return menuDict;
            //});

            var linkedCollectionResource = new
            {
                value = shapedData,
                links
            };

            return Ok(linkedCollectionResource);
        }
        [Authorize]
        [HttpPost(Name = nameof(CreateCalligraphy))]
        //[RequestHeaderMatchesMediaType("Content-Type", "application/json", "multipart/form-data;", "application/vnd.wuther.calligraphyforcreation+json")]
        //[Consumes("application/json", "application/vnd.wuther.calligraphyforcreation+json")]
        public async Task<ActionResult<Calligraphy>> CreateCalligraphy()
        {
            var token = HttpContext.Request.Headers["Authorization"].ToString();
            var apiClient = new HttpClient();

            var files = HttpContext.Request.Form.Files;
            apiClient.SetBearerToken(token.Remove(0, 6));
            var identityServerAddress = _configuration["ConnectionStrings:IdentityServer4Url"];
            var response = await apiClient.GetAsync($"{identityServerAddress}/connect/userinfo");
            if (!response.IsSuccessStatusCode)
            {
                return Unauthorized();
            }
            var content = await response.Content.ReadAsStringAsync();
            var userInfo = JsonSerializer.Deserialize<IdentityUserInfo>(content);

            var writeTime = HttpContext.Request.Form["WriteTime"];
            var title = HttpContext.Request.Form["Title"];

            var path = _configuration.GetSection("UiPath").GetSection("BlogPath").Value;
            var filePath = Path.Combine(path, userInfo.preferred_username, "calligraphy");
            if (!FileHelper.IsExistDirectory(filePath))
            {
                FileHelper.CreateDirectory(filePath);
            }
            foreach (IFormFile file in files)
            {
                var Url = $"\\{userInfo.preferred_username}\\calligraphy\\{file.FileName}";
                //上传图片
                var filename = Path.Combine(filePath, $"{file.FileName}");
                using (FileStream stream = System.IO.File.Create(filename))
                {
                    file.CopyTo(stream);
                    stream.Position = 0;
                    var fileName = file.FileName;
                    stream.Flush();
                }

                var entity = new Calligraphy();
                entity.CreateTime = DateTime.Now;
                entity.Title = title;
                entity.WriteTime = DateTime.Parse(writeTime);
                entity.Path = Url;
                entity.UserAccount = userInfo.preferred_username;
                entity.UserName = userInfo.name;
                await _calligraphyRepository.InsertAsync(entity);
            }
            return Ok();
        }

        private IEnumerable<LinkDto> CreateLinksForCalligraphys(int menuId, string fields)
        {
            var links = new List<LinkDto>();

            if (string.IsNullOrWhiteSpace(fields))
            {
                links.Add(new LinkDto(Url.Link(nameof(GetCalligraphy), new { menuId }),
                    "self",
                    "GET"));
            }
            else
            {
                links.Add(new LinkDto(Url.Link(nameof(GetCalligraphy), new { menuId, fields }),
                    "self",
                    "GET"));
            }
            //links.Add(new LinkDto(Url.Link(nameof(DeleteMenu), new { userId }), "delete menu", "DELETE"));

            //links.Add(new LinkDto(Url.Link(nameof(CreateMenu), new { }), "create menu", "POST"));

            links.Add(new LinkDto(Url.Link(nameof(GetCalligraphys), new { }), "get menus", "GET"));
            return links;
        }

        private IEnumerable<LinkDto> CreateLinksForMenus(DtoCalligraphyParameter parameters, bool hasPrevious, bool hasNext)
        {
            var links = new List<LinkDto>();


            links.Add(new LinkDto(CreateCalligraphysResourceUri(parameters, ResourceUriType.CurrentPage),
                "self", "GET"));

            if (hasPrevious)
            {
                links.Add(new LinkDto(CreateCalligraphysResourceUri(parameters, ResourceUriType.PreviousPage),
                    "previous_page", "GET"));
            }

            if (hasNext)
            {
                links.Add(new LinkDto(CreateCalligraphysResourceUri(parameters, ResourceUriType.NextPage),
                    "next_page", "GET"));
            }

            return links;
        }

        private string CreateCalligraphysResourceUri(DtoCalligraphyParameter parameters,
            ResourceUriType type)
        {
            switch (type)
            {
                case ResourceUriType.PreviousPage:
                    return Url.Link(nameof(GetCalligraphys), new
                    {
                        fields = parameters.Fields,
                        pageNumber = parameters.PageNumber - 1,
                        pageSize = parameters.PageSize,
                        userAccount = parameters.UserAccount,
                        orderBy = parameters.OrderBy
                    });

                case ResourceUriType.NextPage:

                    return Url.Link(nameof(GetCalligraphys), new
                    {
                        fields = parameters.Fields,
                        pageNumber = parameters.PageNumber + 1,
                        pageSize = parameters.PageSize,
                        userAccount = parameters.UserAccount,
                        orderBy = parameters.OrderBy
                    });

                case ResourceUriType.CurrentPage:
                default:
                    return Url.Link(nameof(GetCalligraphys), new
                    {
                        fields = parameters.Fields,
                        pageNumber = parameters.PageNumber,
                        pageSize = parameters.PageSize,
                        userAccount = parameters.UserAccount,
                        orderBy = parameters.OrderBy
                    });
            }
        }
    }
}
